import type { Metadata } from 'next'; import Link from 'next/link'; import { notFound } from 'next/navigation'; import { Bars, Donut, HBars, StackedBars } from '@/components/charts/charts'; import { Block, EventsList, HeroFacts, KpiStrip, PlannedUnavailable, ScrollTable, Tag, entityMetadata } from '@/components/entities/shared'; import { ConstellationsMiniTable, LaunchesTable, OperatorsMiniTable, SatellitesTable } from '@/components/entities/tables'; import { WorldMap } from '@/components/map/world-map'; import { Container } from '@/components/ui/section'; import { Unavailable } from '@/components/ui/unavailable'; import { ApiError, api, safe } from '@/lib/api'; import { fmtDate, fmtDateTime, fmtInt, num, titleCase } from '@/lib/format'; import { MISSION_LABELS, OBJECT_TYPE_LABELS, ORBIT_CLASS_COLORS, SITE_URL, routes } from '@/lib/site'; import type { CountryDetail } from '@/lib/types'; type Props = { params: Promise<{ slug: string }> }; async function load(slug: string): Promise<{ d: CountryDetail; generatedAt: string } | null> { try { const res = await api.country(slug); return { d: res.data, generatedAt: res.meta.generated_at }; } catch (e) { if (e instanceof ApiError && e.notFound) return null; throw e; } } export async function generateMetadata({ params }: Props): Promise { const { slug } = await params; const r = await load(slug).catch(() => null); if (!r) return { title: 'Country not found', robots: { index: false } }; const { d } = r; return entityMetadata({ title: `${d.name} in orbit — ${fmtInt(d.active_payloads)} active satellites, ${fmtInt(d.objects_on_orbit)} objects`, description: `${d.name}: ${fmtInt(d.active_payloads)} active payloads, ${fmtInt(d.objects_on_orbit)} objects on orbit including ${fmtInt(d.debris_on_orbit)} debris fragments, ${fmtInt(d.launches)} launches and ${fmtInt(d.operators)} operators. Rankings, orbital and mission distribution, growth, launch sites and recent launches.`, path: routes.country(d.slug), }); } function Rank({ n, of }: { n: unknown; of: number | null }) { const v = num(n as never); if (v === null) return <>—; return ( <> #{v} {of !== null && of {of}} ); } export default async function CountryPage({ params }: Props) { const { slug } = await params; const [r, all] = await Promise.all([load(slug), safe(api.countries())]); if (!r) notFound(); const { d, generatedAt } = r; const nCountries = all?.data.length ?? null; const orbitData = d.orbit_distribution.map((o) => ({ label: o.orbit_class, value: num(o.count) ?? 0, color: ORBIT_CLASS_COLORS[o.orbit_class] ?? 'var(--other)' })).filter((x) => x.value > 0).sort((a, b) => b.value - a.value); const missionData = d.mission_distribution.map((m) => ({ label: MISSION_LABELS[m.mission_type] ?? titleCase(m.mission_type), value: num(m.count) ?? 0 })).filter((x) => x.value > 0).sort((a, b) => b.value - a.value); const payloadGrowth = d.growth.map((g) => { const p = num(g.payloads) ?? 0; const still = Math.min(p, num(g.still_active) ?? 0); return { x: g.year, still_active: still, retired: Math.max(0, p - still) }; }); const launchGrowth = d.growth.map((g) => ({ x: g.year, y: num(g.launches) ?? 0 })); const sites = d.launch_sites.filter((s) => s.latitude !== null && s.longitude !== null); const objectTypes = [...d.object_type_distribution].sort((a, b) => (num(b.on_orbit) ?? 0) - (num(a.on_orbit) ?? 0)); const jsonLd = { '@context': 'https://schema.org', '@type': 'Country', name: d.name, identifier: d.iso3 ?? d.code, url: `${SITE_URL}${routes.country(d.slug)}`, }; return (